#!/bin/bash

#
# Generate the necessary information for resolving line-number information
# to function names in C files that reference a certain symbol (by default
# mrp_debug). This is used to convert file:line information to function
# names while _listing_ debug sites.
#


error () {
    echo "error: $*" 1>&2
}

warning () {
    echo "warning: $*" 1>&2
}

info () {
    echo "$*" 1>&2
}

usage () {
    info "usage: $0 [-s <symbol>] [-q] -o <output> <inputs>"
    exit ${1:-1}
}

emit () {
    local _opts

    if [ "$1" = "-n" ]; then
        _opts="-n"
        shift
    else
        _opts=""
    fi
    if [ "$1" = "-e" ]; then
        _opts="$_opts -e"
        shift
    fi

    echo $_opts "$*" >> $OUTPUT
}

emit_preamble() {
    emit "#include <stdlib.h>"
    emit "#include <murphy/common/debug.h>"
    emit
}

emit_postamble() {
    emit "#include <murphy/common/debug-auto-register.c>"
}

emit_function_info() {
    prev=""
    cnt=0
    while read func line file; do
        if [ "$prev" != "$file" ]; then
            if [ $cnt -gt 0 ]; then
                emit "    { .line = 0, .func = NULL }"
                emit "};"
                emit "static mrp_debug_file_t file_$(($cnt - 1)) = {"
                emit "    .file = \"$prev\","
                emit "    .info = info_$(($cnt - 1))"
                emit "};"
                emit ""
            fi
            emit "/* $file */"
            emit "static mrp_debug_info_t info_$cnt[] = {"
            cnt=$(($cnt + 1))
            prev=$file
        fi
        emit "    { .line = $line, .func = \"$func\" },"
    done
    if [ $cnt -gt 0 ]; then
        emit "    { .line = 0, .func = NULL }"
        emit "};"
        emit "static mrp_debug_file_t file_$(($cnt - 1)) = {"
        emit "    .file = \"$prev\","
        emit "    .info = info_$(($cnt - 1))"
        emit "};"
        emit ""
    fi
    emit "/* table of all files */"
    emit "static mrp_debug_file_t *debug_files[] = {"
    i=0
    while [ $i -lt $cnt ]; do
        emit "    &file_$i,"
        i=$(($i + 1))
    done
    emit "    NULL"
    emit "};"
    emit ""
}

emit_no_function_info() {
    emit "/* No ctags found, could not generate debug info... */"
    emit "static mrp_debug_file_t *debug_files[] = {"
    emit "    NULL"
    emit "};"
    emit ""
}

# set up defaults
SYMBOL="mrp_debug\("                  # symbol to look for
SOURCE=""                             # default to all file containing $SYMBOL
OUTPUT=""                             # no default output, must be specified

# parse command line
while [ -n "${1#-}" ]; do
    case $1 in
        -o|--output)
            if [ -z "$OUTPUT" ]; then
                shift
                OUTPUT="$1"
            else
                error "Multiple output files requested."
                usage
            fi
            ;;
        -s|--symbol)
	    shift;
           SYMBOL="$1"
            ;;
	-v|--verbose)
	    VERBOSE="yes"
	    ;;
        -h|--help)
            usage 0
            ;;
        -*)
            error "Unknown option '$1'."
            usage
            ;;
        *)
            SOURCE="$SOURCE $1"
            ;;
    esac
    shift
done

# check that we've got everything mandatory
if [ -z "$OUTPUT" ]; then
    error "No output file specified (use the -o option)."
    usage
fi
if [ -z "$SYMBOL" ]; then
    SYMBOL="mrp_debug\("
fi
if [ -z "$SOURCE" ]; then
    [ -n "VERBOSE" ] && info "Scanning all C files containing $SYMBOL..."
    SOURCE="`find . -name \*.c -exec grep -Hn $SYMBOL\( {} \; | \
                  cut -d ':' -f 1 | sort -u`"
fi

[ -n "$VERBOSE" ] && info "Generating debug function info file $OUTPUT..."
rm -f $OUTPUT
touch $OUTPUT

# generate the output
emit_preamble

which ctags >& /dev/null
if [ "$?" = "0" ]; then
    ctags -x --c-kinds=f $SOURCE | tr -s '\t' ' ' | \
        cut -d ' '  -f 1,3,4 | sort -k 3,3 -k 2,2n | emit_function_info
else
    warning "No ctags found, generating empty debug tables..."
    emit_no_function_info
fi

emit_postamble
